#include "container/list.h"
#include <stdio.h>
#include <string.h>
#include "unity.h"


void test_ListInit(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[10];
    cotList_Init(&list, nodeBuf, 10);

    TEST_ASSERT_EQUAL_PTR(nodeBuf, list.pNodeBuf);
    TEST_ASSERT_EQUAL_INT(10, list.nodeBufNum);
    TEST_ASSERT_EQUAL_PTR(&list.node, list.node.pPrev);
    TEST_ASSERT_EQUAL_PTR(&list.node, list.node.pNext);
}

void test_ListInsertAndAccess(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_Insert(&list, cotList_End(&list), &data1);
    cotList_Insert(&list, cotList_End(&list), &data2);
    cotList_Insert(&list, cotList_End(&list), &data3);

    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(3, cotList_Size(&list));
}

void test_ListPushAndPop(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushFront(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(3, cotList_Size(&list));

    cotList_PopFront(&list);
    cotList_PopBack(&list);

    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list));
}

void test_ListIteration(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    int sum = 0;
    for_list_each(item, list)
    {
        int* value = item_ptr(int, item);
        sum += *value;
    }

    TEST_ASSERT_EQUAL_INT(60, sum);
}


void test_ListClear(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    TEST_ASSERT_EQUAL_INT(3, cotList_Size(&list));

    cotList_Clear(&list);

    TEST_ASSERT_EQUAL_INT(0, cotList_Size(&list));
    TEST_ASSERT_TRUE(cotList_Empty(&list));
    TEST_ASSERT_NULL(cotList_Front(&list));
    TEST_ASSERT_NULL(cotList_Back(&list));
}


void test_ListErase(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    cotListItem_t item;

    TEST_ASSERT_NULL(cotList_Erase(&list, &item));

    cotListItem_t* item2 = cotList_Begin(&list)->pNext;

    cotList_Erase(&list, item2);

    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));

    cotListItem_t* item3 = cotList_Begin(&list)->pNext;

    cotList_Erase(&list, item3);

    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Back(&list));

    cotList_Erase(&list, cotList_Begin(&list));

    TEST_ASSERT_EQUAL_INT(0, cotList_Size(&list));
    TEST_ASSERT_NULL(cotList_Front(&list));
    TEST_ASSERT_NULL(cotList_Back(&list));
}

void test_ListRemove(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[5];
    cotList_Init(&list, nodeBuf, 5);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);
    cotList_PushBack(&list, &data2);

    TEST_ASSERT_EQUAL_INT(4, cotList_Size(&list));

    cotList_Remove(&list, &data2);

    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));

    cotList_Remove(&list, &data3);

    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Back(&list));

    cotList_Remove(&list, &data1);

    TEST_ASSERT_EQUAL_INT(0, cotList_Size(&list));
    TEST_ASSERT_NULL(cotList_Front(&list));
    TEST_ASSERT_NULL(cotList_Back(&list));
}

bool OnRemoveCondition(const void *pData)
{
    int *pVal = (int *)pData;

    if (*pVal == 20)
    {
        return true;
    }

    return false;
}

void test_ListRemoveIf(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[5];
    cotList_Init(&list, nodeBuf, 5);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    TEST_ASSERT_EQUAL_INT(4, cotList_Size(&list));

    cotList_RemoveIf(&list, OnRemoveCondition);

    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));

    cotList_Remove(&list, OnRemoveCondition);

    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));
}

void test_ListSwap(void)
{
    cotList_t list1;
    cotListItem_t nodeBuf1[3];
    cotList_Init(&list1, nodeBuf1, 3);

    cotList_t list2;
    cotListItem_t nodeBuf2[3];
    cotList_Init(&list2, nodeBuf2, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list1, &data1);
    cotList_PushBack(&list1, &data2);

    cotList_PushBack(&list2, &data3);

    cotList_Swap(&list1, &list2);

    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list1));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Front(&list1));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list1));

    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list2));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list2));
    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Back(&list2));
}

void test_ListEmpty(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    TEST_ASSERT_TRUE(cotList_Empty(&list));

    int data = 10;
    cotList_PushFront(&list, &data);

    TEST_ASSERT_FALSE(cotList_Empty(&list));
}

void test_ListSize(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    TEST_ASSERT_EQUAL_INT(0, cotList_Size(&list));

    int data1 = 10;
    cotList_PushFront(&list, &data1);

    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list));

    int data2 = 20;
    cotList_PushFront(&list, &data2);

    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list));
}

void test_ListInsertItem(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    cotListItem_t item1;
    item1.pData = &data1;

    int data2 = 20;
    cotListItem_t item2;
    item2.pData = &data2;

    int data3 = 30;
    cotListItem_t item3;
    item3.pData = &data3;

    cotList_InsertItem(&list, cotList_End(&list), &item1);
    cotList_InsertItem(&list, cotList_End(&list), &item2);
    cotList_InsertItem(&list, cotList_End(&list), &item3);

    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(3, cotList_Size(&list));
}

void test_ListReverseIteration(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    int sum = 0;
    for_list_each_r(item, list)
    {
        int* value = item_ptr(int, item);
        sum += *value;
    }

    TEST_ASSERT_EQUAL_INT(60, sum);
}

void test_ListOutOfBoundsAccess(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    cotList_PushBack(&list, &data1);

    TEST_ASSERT_NOT_NULL(cotList_Begin(&list)->pPrev);
    TEST_ASSERT_NOT_NULL(cotList_End(&list)->pNext);
    TEST_ASSERT_NOT_NULL(cotList_Next(cotList_End(&list)));
    TEST_ASSERT_NOT_NULL(cotList_rBegin(&list)->pNext);
    TEST_ASSERT_NOT_NULL(cotList_rEnd(&list)->pPrev);
    TEST_ASSERT_NOT_NULL(cotList_rNext(cotList_rEnd(&list)));
}

void test_ListInsertEmpty(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data = 10;
    cotList_Insert(&list, cotList_End(&list), &data);

    TEST_ASSERT_EQUAL_PTR(&data, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list));
}

void test_ListInsertItemNull(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    cotListItem_t item;
    item.pData = NULL;

    cotList_InsertItem(&list, cotList_End(&list), &item);

    TEST_ASSERT_TRUE(cotList_Empty(&list));
}

void test_ListInsertMultipleItems(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[5];
    cotList_Init(&list, nodeBuf, 5);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;
    int data4 = 40;

    cotList_Insert(&list, cotList_End(&list), &data1);
    cotList_Insert(&list, cotList_End(&list), &data2);
    cotList_Insert(&list, cotList_Begin(&list), &data3);
    cotList_Insert(&list, cotList_Begin(&list), &data4);

    TEST_ASSERT_EQUAL_PTR(&data4, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(4, cotList_Size(&list));
}

void test_ListPopFront(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);

    cotList_PopFront(&list);

    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list));

    cotList_PopFront(&list);

    TEST_ASSERT_TRUE(cotList_Empty(&list));
    TEST_ASSERT_NULL(cotList_Front(&list));
    TEST_ASSERT_NULL(cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(0, cotList_Size(&list));
}

void test_ListPopBack(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);

    cotList_PopBack(&list);

    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list));

    cotList_PopBack(&list);

    TEST_ASSERT_TRUE(cotList_Empty(&list));
    TEST_ASSERT_NULL(cotList_Front(&list));
    TEST_ASSERT_NULL(cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(0, cotList_Size(&list));
}

void test_ListSwapEmpty(void)
{
    cotList_t list1;
    cotListItem_t nodeBuf1[3];
    cotList_Init(&list1, nodeBuf1, 3);

    cotList_t list2;
    cotListItem_t nodeBuf2[3];
    cotList_Init(&list2, nodeBuf2, 3);

    cotList_Swap(&list1, &list2);

    TEST_ASSERT_TRUE(cotList_Empty(&list1));
    TEST_ASSERT_TRUE(cotList_Empty(&list2));
}

void test_ListSwapNonEmpty(void)
{
    cotList_t list1;
    cotListItem_t nodeBuf1[3];
    cotList_Init(&list1, nodeBuf1, 3);

    int data1 = 10;
    int data2 = 20;

    cotList_PushBack(&list1, &data1);
    cotList_PushBack(&list1, &data2);

    cotList_t list2;
    cotListItem_t nodeBuf2[3];
    cotList_Init(&list2, nodeBuf2, 3);

    int data3 = 30;

    cotList_PushBack(&list2, &data3);

    cotList_Swap(&list1, &list2);

    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Front(&list1));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list1));
    TEST_ASSERT_EQUAL_INT(1, cotList_Size(&list1));

    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list2));
    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Back(&list2));
    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list2));
}

void test_ListSwapNonEmptyAndEmpty(void)
{
    cotList_t list1;
    cotListItem_t nodeBuf1[3];
    cotList_Init(&list1, nodeBuf1, 3);

    int data1 = 10;
    int data2 = 20;

    cotList_PushBack(&list1, &data1);
    cotList_PushBack(&list1, &data2);

    cotList_t list2;
    cotListItem_t nodeBuf2[3];
    cotList_Init(&list2, nodeBuf2, 3);

    cotList_Swap(&list1, &list2);

    TEST_ASSERT_TRUE(cotList_Empty(&list1));
    TEST_ASSERT_EQUAL_INT(0, cotList_Size(&list1));

    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list2));
    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Back(&list2));
    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list2));
}

void test_ListEraseFront(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    cotList_Erase(&list, cotList_Begin(&list));

    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data3, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list));
}

void test_ListEraseBack(void)
{
    cotList_t list;
    cotListItem_t nodeBuf[3];
    cotList_Init(&list, nodeBuf, 3);

    int data1 = 10;
    int data2 = 20;
    int data3 = 30;

    cotList_PushBack(&list, &data1);
    cotList_PushBack(&list, &data2);
    cotList_PushBack(&list, &data3);

    cotList_Erase(&list, cotList_rBegin(&list));

    TEST_ASSERT_EQUAL_PTR(&data1, cotList_Front(&list));
    TEST_ASSERT_EQUAL_PTR(&data2, cotList_Back(&list));
    TEST_ASSERT_EQUAL_INT(2, cotList_Size(&list));
}


int test_List(void) 
{
    UNITY_BEGIN();  // 启动测试

    RUN_TEST(test_ListInit);
    RUN_TEST(test_ListInsertAndAccess);
    RUN_TEST(test_ListPushAndPop);
    RUN_TEST(test_ListIteration);

    RUN_TEST(test_ListClear);
    RUN_TEST(test_ListErase);
    RUN_TEST(test_ListRemove);
    RUN_TEST(test_ListRemoveIf);
    RUN_TEST(test_ListSwap);

    RUN_TEST(test_ListEmpty);
    RUN_TEST(test_ListSize);
    RUN_TEST(test_ListInsertItem);
    RUN_TEST(test_ListReverseIteration);
    
    RUN_TEST(test_ListOutOfBoundsAccess);
    RUN_TEST(test_ListInsertEmpty);
    RUN_TEST(test_ListInsertItemNull);

    RUN_TEST(test_ListInsertMultipleItems);
    RUN_TEST(test_ListPopFront);
    RUN_TEST(test_ListPopBack);


    RUN_TEST(test_ListSwapEmpty);
    RUN_TEST(test_ListSwapNonEmpty);
    RUN_TEST(test_ListSwapNonEmptyAndEmpty);
    
    RUN_TEST(test_ListEraseFront);
    RUN_TEST(test_ListEraseBack);

    UNITY_END();  // 结束测试

    return 0;
}  

